Repository navigation
feat(explorer): read age & activity filters both ways, and sort by most chosen - #686
Conversation
… for the memory readers 00112 widens lorekit_match_retention with opened_within_days and three min_* floors, appends them to the list/facets/activity/pivot RPCs, and lets lorekit_memory_list sort by opened_count with an integer keyset cursor. migrations.test.sql gains section 112; section 108 now derives the helper signature it checks grants against. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…count on the memory reads The list/facets/activity/pivot schemas gain opened_within_days and min_seen/read/opened_count (read-only; GroomConditions is unchanged), the edge retention mapping forwards them, and the list paginates the new opened_count sort through a separate integer cursor codec mirrored from mcp-core so paginate.ts and its MCP byte-mirror stay untouched. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…r by most chosen Recurrence, Delivered, Chosen and Last agent open gain an At most/At least (More than/Within) toggle on their value list; presets and typed values apply in the selected direction and a row holds one direction at a time. The reverse directions are Explorer-only, so Create retention policy is hidden while one is set. A Sort control (Newest / Most chosen) backs ?sort= and threads opened_count to the list route. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
… their decision Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
|
The dashboard preview for this PR — redeployed on each push that changes the web app.
Preview always points at this PR's latest commit; Deployment is the immutable build of |
…floor that narrows, menu chrome budget - SortSelect is an icon-sized square on mobile (transparent native select over the icon) so the search box keeps its room. - min_seen_count starts at 2: seen_count >= 1 is a table constraint, so a floor of 1 matched every memory. - FilterMenu budgets the direction toggle / Status row in its chrome height. - Pivot's OpenAPI description names the reverse directions; the grooming doc no longer prints a URL spelling the JSON-encoded ?sort= state does not use. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
deeplink.test.mjs pins LORE_PARAM_DEFAULTS to the Explorer's useUrlState params, so the new sort param needs its default (null = Newest) and a slot in PARAM_ORDER. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
… and accept >N / <N The At most/At least toggle was a mode that hid half the values. Each two-way row's value list now shows both directions' presets under two headings, a bare number offers both directions, and >N / <N (also >=, <=, ≥, ≤) pick one side. A strict comparison on a count is stored as the next whole number (>40 -> 41 times or more) so the row reads as the filter it applies; days are not shifted. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
✅ No issues foundZero-delta re-review at 869f11b: no code changed since the last full pass. The edited PR body now cites 00113/§113 and the two-direction rows, so the description warning clears. CI green, no open threads. Checked: 0 of 33 dependent files traced Progress: open review threads 2 → 1 → 0 → 0 across the last 4 reviews What this change reaches — 48 changed exports · 33 dependent files · 0 checked · 33 not checked · 4 changed files imported by 47 filesflowchart LR
subgraph pr["Changed in this PR"]
s1["retentionConditionPlaceholder<br/>signature changed"]:::changed
s2["retentionFieldMatches<br/>signature changed"]:::changed
s3["RetentionConditions<br/>body changed"]:::changed
s4["FilterMenu<br/>body changed"]:::changed
s5["normalizeRetentionConditions<br/>body changed"]:::changed
s6["rangeTelemetry<br/>body changed"]:::changed
smore["+42 more changed exports<br/>listed below"]:::changed
m1["supabase/functions/_shared/schemas/memory.ts<br/>file changed"]:::changed
m2["packages/web/src/lib/analytics/track.ts<br/>file changed"]:::changed
m3["packages/schemas/src/domain/memory.ts<br/>file changed"]:::changed
m4["packages/web/src/components/lore/RetentionPill.tsx<br/>file changed"]:::changed
end
s1c1["packages/web/src/components/settings/GroomingRuleBuilder.tsx<br/>? not checked"]:::unknown
s1c2["packages/web/src/lib/retention-filter.spec.ts<br/>? not checked"]:::unknown
s2c1["packages/web/src/components/lore/FilterMenu.tsx<br/>? not checked"]:::unknown
s2c2["packages/web/src/lib/retention-filter.spec.ts<br/>? not checked"]:::unknown
s3c1["packages/web/src/components/lore/ · 7 files<br/>? not checked"]:::unknown
s3c2["packages/web/src/components/settings/GroomingRuleBuilder.tsx<br/>? not checked"]:::unknown
s3c3["+4 more files<br/>? not checked"]:::unknown
s4c1["packages/web/src/components/lore/ · 4 files<br/>? not checked"]:::unknown
s5c1["packages/web/src/components/lore/ · 2 files<br/>? not checked"]:::unknown
s5c2["packages/web/src/lib/retention-filter.spec.ts<br/>? not checked"]:::unknown
s5c3["packages/web/src/lib/queries/lore.ts<br/>? not checked"]:::unknown
s6c1["packages/web/src/lib/analytics/ · 2 files<br/>? not checked"]:::unknown
s6c2["packages/web/src/components/insights/InsightsPage.tsx<br/>? not checked"]:::unknown
s6c3["packages/web/src/components/lore/LoreExplorer.tsx<br/>? not checked"]:::unknown
m1i1["supabase/functions/memories/handlers/ · 17 files<br/>import this file"]:::imports
m1i2["supabase/functions/_shared/schemas/ · 2 files<br/>import this file"]:::imports
m1i3["supabase/functions/_shared/schemas/openapi/spec.ts<br/>imports this file"]:::imports
m2i1["packages/web/src/components/lore/ · 7 files<br/>import this file"]:::imports
m2i2["packages/web/src/components/landing/ · 4 files<br/>import this file"]:::imports
m2i3["packages/web/src/lib/ · 2 files<br/>import this file"]:::imports
m2i4["packages/web/src/components/command/CommandPaletteProvider.tsx<br/>imports this file"]:::imports
m2i5["packages/web/src/components/content/ReadingTelemetry.tsx<br/>imports this file"]:::imports
m2i6["+4 more files<br/>import this file"]:::imports
m3i1["packages/schemas/src/domain/ · 3 files<br/>import this file"]:::imports
m3i2["packages/schemas/src/shared/ · 2 files<br/>import this file"]:::imports
m3i3["packages/schemas/src/index.ts<br/>imports this file"]:::imports
m3i4["packages/schemas/src/openapi/spec.ts<br/>imports this file"]:::imports
m4i1["packages/web/src/components/lore/FilterBar.tsx<br/>imports this file"]:::imports
s1 -.-> s1c1
s1 -.-> s1c2
s2 -.-> s2c1
s2 -.-> s2c2
s3 -.-> s3c1
s3 -.-> s3c2
s3 -.-> s3c3
s4 -.-> s4c1
s5 -.-> s5c1
s5 -.-> s5c2
s5 -.-> s5c3
s6 -.-> s6c1
s6 -.-> s6c2
s6 -.-> s6c3
m1 -.-> m1i1
m1 -.-> m1i2
m1 -.-> m1i3
m2 -.-> m2i1
m2 -.-> m2i2
m2 -.-> m2i3
m2 -.-> m2i4
m2 -.-> m2i5
m2 -.-> m2i6
m3 -.-> m3i1
m3 -.-> m3i2
m3 -.-> m3i3
m3 -.-> m3i4
m4 -.-> m4i1
classDef changed fill:#eef2ff,stroke:#6366f1,color:#1e1b4b
classDef ok fill:#e7f6ec,stroke:#16a34a,color:#14532d
classDef partial fill:#fef9c3,stroke:#ca8a04,color:#713f12
classDef unknown fill:#f3f4f6,stroke:#9ca3af,stroke-dasharray:4 3,color:#374151
classDef bad fill:#fde8e8,stroke:#dc2626,color:#7f1d1d
classDef warn fill:#fff4e5,stroke:#d97706,color:#78350f
classDef imports fill:#f8fafc,stroke:#64748b,stroke-dasharray:4 3,color:#1e293b
Review details
Found Quality — produced 0 → posted inline 0 · cleared 0 · carried forward 0 · deferred 0 · below-bar 0 Run incremental · 0 lines in delta · tier deep · depth checkout Nothing to report — standards (skipped), optimality (skipped), measurability (skipped), severity, 0 files skipped.
|
… a story comment Addresses @dash0-dev's comment: #686 (comment) Refs: #686 Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…ches Addresses the pr-reviewer comment: #686 (comment) Refs: #686 Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
…pped 00112 #680 merged first with 00112_relevant_ranking.sql, so this PR's migration, its migrations.test.sql section and every reference to them move to 00113 / section 113, as the review thread on the migration agreed. Co-authored-by: dash0-dev[bot] <257284812+dash0-dev[bot]@users.noreply.github.com>
Summary
The Explorer's Age & activity filters only point the retention way ("N or fewer", "not opened for N days"), so there was no way to see the most-used lore. Recurrence, Delivered, Chosen and Last agent open now read both ways, and the list can be sorted by Most chosen.
Changes
At mostandAt least, orMore thanandWithin, under two headings), with no toggle or mode. A typed40offers both directions;>40/<40(also>=,<=,≥,≤) pick one side. A strict comparison on a count is stored as the next whole number (>40→41 times or more); days are not shifted. A row holds one direction at a time (picking one clears the other). Pills read e.g.Chosen: 10 times or more.Newest | Most chosencontrol in the toolbar (?sort=opened_count), threaded throughuseMemories→POST /memories/list.GroomConditionsSchemadoesn't gain them,retentionConditionsToGroomConditionsdrops them, and "Create retention policy" is hidden while one is set (a policy would archive a different set from the one on screen).opened_within_days,min_seen_count,min_read_count,min_opened_count(each floor starts at the first value that narrows: 1, or 2 formin_seen_count), andsort=opened_counton the list. The count sort pages through a separate integer cursor (mcp-core/src/rest/count-cursor.ts, mirrored to_shared/api/), sopaginate.tsand its MCP byte-mirror are untouched.00112):lorekit_match_retentiongoes from 10 to 14 args. The four readers are dropped at their exact old signatures and recreated with the new params appended. The list also takesp_cursor_countandp_sort => 'opened_count'.opened_within_daysdoes not use 00100's never-opened fallback. The migration also restores theanonrevoke onlorekit_memory_listthat 00105's drop-and-recreate lost.migrations.test.sql§113 (all four readers, never-opened exclusion, keyset continuity across a tie, ACLs, single overload). §108's helper ACL check now derives its signature. Also schema, codec, model and explorer-sort specs, a Storybook interaction test for the two-direction value list, and a spec that a direction switch still names its field inlore.filter_changed.llms.txt, OpenAPI descriptions,memories/CLAUDE.md,docs/decisions.md+ root CLAUDE.md key decision,docs/key-files.md,docs/otel.md(lore.sort_changed).Verification
869f11b6, after mergingmain): Integration smoke applied feat(relevant): grade lexical relevance with ts_rank_cd and cut the candidate window by rank #680's00112then this PR's00113and passed everymigrations.test.sqlassertion, §113 included. Storybook interaction + visual (including the one-list story), typecheck/test/lint (affected), migration order and edgedeno checkalso pass.count-cursor+ edge parity, web model/analytics/sort specs,tscfor web, CLI deep-link test, ESLint on changed files (no new findings).review-loopran threepr-reviewerpasses. It fixed a direction switch dropping the analyticsfieldand a stale story comment, and answered the migration-number question (then renumbered once feat(relevant): grade lexical relevance with ts_rank_cd and cut the candidate window by rank #680 landed). No open threads remain.?sort=param, solorekit linkand the Explorer's URL params stay in step.🧪 UI verification spec — run against the preview deployment
Target: preview
Format: intent
Spec 1: A user reads Chosen both ways and swaps the direction from the same list
Changed: the Chosen entry's value list under Age & activity in the Filter menu, which now lists an At most group and an At least group
Start: /lore
Preconditions:
retention-policiesflag is on (its default), so the Age & activity rows render.Steps:
Expected:
Hints:
Out of scope:
Spec 2: A user types a comparison into Chosen and gets the matching direction
Changed: the Chosen value list's search box, which also accepts a typed number with an optional
>/<comparisonStart: /lore
Preconditions:
Steps:
40into the search box.>40and press Enter.Expected:
40typed, the list offers both "40 times or fewer" and "40 times or more", each labelled "custom".>40typed, the list offers "41 times or more" labelled "custom", and no row ending in "or fewer".Hints:
Spec 3: A user sorts the Explorer by most chosen
Changed: the Sort control beside the date picker in the Lore Explorer toolbar (Newest | Most chosen)
Start: /lore
Preconditions:
Steps:
Expected:
Hints:
Out of scope: